iT邦幫忙

2023 iThome 鐵人賽

DAY 28
1
Software Development

Rust Web API 從零開始系列 第 28

Day28 - 附錄: Rust中的非同步程式設計(2)

  • 分享至 

  • xImage
  •  

昨天我們提到,C#中的Task是以背景執行的任務排程器透過一定的機制去輪詢(Poll)執行中的Task狀態,進一步的介紹可以看一下TaskScheduler的文件說明。

以昨天的while為例,這種瘋狂詢問狀態的做法,不可避免的會產生無效的詢問(詢問的時候Task尚未完成),要讓非同步程式高效的作法一個要點,就是盡可能讓調度程式在剛剛好的時機點詢問狀態,接下來就要介紹Rust是如何作到的:

SimpleFuture

Async-Book中採取了一個化簡過後的SimpleFuture:

trait SimpleFuture {
    type Output;
    fn poll(&mut self, wake: fn()) -> Poll<Self::Output>;
}

enum Poll<T> {
    Ready(T),
    Pending,
}

另外我們來看一個簡短的程式碼:

fn main(){
    example_runtime::block_on(async_method);
}

async fn async_method() -> impl SimpleFuture<()> {
    todo!();
}

上面程式碼中,example_runtime會提供一個非同步執行器executor來處理async_method回傳的SimpleFuture物件,executor會呼叫SimpleFuture提供的poll方法,這個行為就相當於C#中的TaskScheduler詢問Task的執行狀態,通常情況下這個SimpleFuture可以大概實作成下面這樣:

pub struct TestFuture
{
    status: bool
}

impl SimpleFuture for TestFuture {
    type Output = ();

    fn poll(&mut self, wake: fn()) -> Poll<Self::Output> {
        if self.status {
            Poll::Ready(())
        } else {
            self.status = push(wake);
            Poll::Pending
        }
    }
}

在這個實作中,執行器調用poll時,如果future已經完成了就會回傳Ready,如果沒有的話就會使用由執行器傳入的wake方法來推進整個任務的進行。

依賴反轉與CallBack

wake是什麼東西呢?讓我們從C#的例子來看,當排程器在處裡一個Task的時候,除非詢問他的狀態,否則無法知道任務是否完成,而Task本身需要被動的等待詢問。而Future trait的設計上則會在執行器輪詢的時候拿到執行器提供的方法,也就是說當任務有進展的時候,可以透過這個開口通知執行器,主動接受執行器的輪詢。

當然,非同步程式並沒有那麼簡單,rust作為一個標榜安全性的語言,實際上的future trait是長這個樣子:

pub trait Future {
    type Output;
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}

前面也有提到Pin是用於固定記憶體位置,而Context就是前面fn wake的強化版本,可以用來管理複雜的連線狀況。

這個設計其實很巧妙的定義了future與executor的關係,讓兩者的溝通由外層直接相依於IsCompleted變成依賴於Context這個抽象的通道,並且透過CallBack的方式反向通知上層邏輯,讓介面兩側的元件各只要關心自己的職責-任務的管理以及任務的進行。

小結

採用無腦輪詢方案會需要為每個任務都準備一個執行緒來監看進度,舉例來說就像是網購的郵局包裹一樣,當包裹寄出之後,購買人需要一直去郵局的網站查詢包裹的貨態,才可以安排時間在家裡等待包裹,非常不經濟。
Rust的Future設計上則是由任務主動通知執行器任務完成,有點像去咖啡店點一杯咖啡,店員會給你一個呼叫器,等到呼叫器響了再去櫃檯拿咖啡就好了,盡可能在需要的時候才去問店員咖啡做好了沒。


上一篇
Day27 - 附錄: Rust中的非同步程式設計(1)
下一篇
Day29 - 附錄: Rust中的非同步程式設計(3)
系列文
Rust Web API 從零開始30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
Hell Kiki
iT邦新手 4 級 ‧ 2023-09-28 12:19:41

很巧妙的比喻

marvinhsu iT邦新手 4 級 ‧ 2023-09-28 23:52:17 檢舉

我花了很多功夫理解rust中非同步的設計
仔細讀懂後真只有讚嘆

我要留言

立即登入留言